package com.abing.starry.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.RandomUtil;
import com.abing.starry.common.ErrorCode;
import com.abing.starry.config.CaptchaProperties;
import com.abing.starry.constant.RedisKeyConstant;
import com.abing.starry.exception.BusinessException;
import com.abing.starry.exception.ThrowUtils;
import com.abing.starry.manager.MailManager;
import com.abing.starry.mapper.UserMapper;
import com.abing.starry.model.entity.UserEntity;
import com.abing.starry.model.enums.UserRoleEnum;
import com.abing.starry.model.request.user.OpsUserRequest;
import com.abing.starry.model.request.user.QueryUserRequest;
import com.abing.starry.model.request.user.UserLoginRequest;
import com.abing.starry.model.vo.LoginCaptchaVO;
import com.abing.starry.service.UserService;
import com.abing.starry.util.EncryptUtils;
import com.abing.starry.util.TokenUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.awt.*;
import java.time.Duration;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import static com.abing.starry.constant.RedisKeyConstant.LOGIN_CAPTCHA;
import static com.abing.starry.constant.UserConstant.USER_LOGIN_STATE;
import static com.abing.starry.constant.UserConstant.USER_MAIL_CAPTCHA_EXPIRE;

/**
 * @Author CaptainBing
 * @Date 2023/11/8 22:24
 * @Description
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {

    private final StringRedisTemplate stringRedisTemplate;

    private final MailManager mailManager;

    private final ThreadPoolExecutor emailThreadPool;

    @Override
    @Deprecated
    public boolean userRegister(UserLoginRequest userLoginRequest) {
        String userAccount = userLoginRequest.getUserAccount();
        String userPassword = userLoginRequest.getUserPassword();

        if (StringUtils.isAnyEmpty(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        ThrowUtils.throwIf(!Validator.isEmail(userAccount),ErrorCode.PARAMS_ERROR,"邮箱格式不合法");
        UserEntity currentUser = this.getOne(Wrappers.lambdaQuery(UserEntity.class)
                                                     .eq(UserEntity::getUserAccount, userAccount));
        if (Objects.nonNull(currentUser)){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"账号已注册");
        }
        synchronized (userAccount.intern()){
            UserEntity registerUser = new UserEntity();
            registerUser.setUserAccount(userAccount);
            registerUser.setUserName(userAccount);
            registerUser.setUserPassword(EncryptUtils.genUserPassword(userPassword));
            boolean saveUser = this.save(registerUser);
            ThrowUtils.throwIf(!saveUser,ErrorCode.OPERATION_ERROR,"用户注册失败");
            return saveUser;
        }
    }

    @Override
    public boolean userLogout() {
        String id = TokenUtils.getCurrentUser().getId();
        return Boolean.TRUE.equals(stringRedisTemplate.delete(USER_LOGIN_STATE + id));
    }

    @Override
    public boolean sendCaptcha(String userAccount) {
        ThrowUtils.throwIf(StringUtils.isEmpty(userAccount),ErrorCode.PARAMS_ERROR,"邮箱账号为空");
        ThrowUtils.throwIf(!Validator.isEmail(userAccount),ErrorCode.PARAMS_ERROR,"邮箱格式不合法");
        String userMailCaptchaExpireKey = USER_MAIL_CAPTCHA_EXPIRE + userAccount;
        boolean userMailCaptchaExist = Boolean.TRUE.equals(stringRedisTemplate.hasKey(userMailCaptchaExpireKey));
        ThrowUtils.throwIf(userMailCaptchaExist, ErrorCode.OPERATION_ERROR, "验证码已发送，一分钟内请勿重复发送");
        CompletableFuture.runAsync(() -> mailManager.sendCaptchaToSomebodyForLogin(userAccount), emailThreadPool);
        return true;
    }

    @Override
    public UserEntity getCurrentUser(){

        UserEntity currentUser = TokenUtils.getCurrentUser();
        String id = String.valueOf(currentUser.getId());
        String cacheToken = stringRedisTemplate.opsForValue().get(USER_LOGIN_STATE + id);
        ThrowUtils.throwIf(StringUtils.isEmpty(cacheToken),ErrorCode.NO_AUTH_ERROR);

        String token = TokenUtils.getToken();
        if (!StringUtils.equals(token,cacheToken)){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        UserEntity userEntity = this.getById(id);
        ThrowUtils.throwIf(Objects.isNull(userEntity),ErrorCode.NOT_FOUND_ERROR);
        return getSafetyUser(userEntity);
    }


    @Override
    public IPage<UserEntity> queryUserByPage(QueryUserRequest queryUserRequest) {

        Page<UserEntity> userEntityPage = this.page(new Page<>(queryUserRequest.getCurrent(), queryUserRequest.getPageSize()),
                                                           Wrappers.lambdaQuery(UserEntity.class)
                                                                   .like(StringUtils.isNotEmpty(queryUserRequest.getUserName()), UserEntity::getUserName, queryUserRequest.getUserName())
                                                                   .like(StringUtils.isNotEmpty(queryUserRequest.getUserAccount()), UserEntity::getUserAccount, queryUserRequest.getUserAccount())
                                                                   .eq(!Objects.isNull(queryUserRequest.getUserRole()), UserEntity::getUserRole, queryUserRequest.getUserRole()));

        List<UserEntity> userEntityList = userEntityPage.getRecords()
                                                        .stream()
                                                        .map(this::getSafetyUser)
                                                        .collect(Collectors.toList());
        userEntityPage.setRecords(userEntityList);
        return userEntityPage;
    }

    @Override
    public LoginCaptchaVO fetchCaptchaToBase64() {

        //定义图形验证码的长、宽、验证码字符数、干扰线宽度
        ShearCaptcha shearCaptcha = CaptchaUtil.createShearCaptcha(150, 40, 4, 4);
//        shearCaptcha.setTextAlpha(0.8F);
        shearCaptcha.setBackground(Color.WHITE);
//        shearCaptcha.setFont(Font.getFont(Font.SANS_SERIF));
        //获取验证码中的文字内容
        String key = RandomUtil.randomString(11);
        String loginCaptchaKey = RedisKeyConstant.getRedisKey(LOGIN_CAPTCHA, key);
        stringRedisTemplate.opsForValue().set(loginCaptchaKey, shearCaptcha.getCode(), Duration.ofSeconds(30));
        String imageBase64Data = shearCaptcha.getImageBase64Data();
        LoginCaptchaVO loginCaptchaVO = new LoginCaptchaVO();
        loginCaptchaVO.setKey(key);
        loginCaptchaVO.setBase64(imageBase64Data);

        return loginCaptchaVO;
    }


    @Override
    public boolean opsUserById(OpsUserRequest opsUserRequest) {

        String opsUserId = opsUserRequest.getOpsUserId();

        ThrowUtils.throwIf(StringUtils.isEmpty(opsUserId),ErrorCode.PARAMS_ERROR);
        String userId = TokenUtils.getCurrentUser().getId();
        ThrowUtils.throwIf(userId.equals(opsUserId),ErrorCode.OPERATION_ERROR,"管理员不能操作自己");

        String userRole = opsUserRequest.getOpsType() == OpsUserRequest.OpsType.ban
                ? UserRoleEnum.BAN.getValue()
                :UserRoleEnum.USER.getValue();

        boolean banStatus = this.update(
                Wrappers.lambdaUpdate(UserEntity.class)
                        .eq(UserEntity::getId, opsUserId)
                        .set(UserEntity::getUserRole, userRole)
        );
        ThrowUtils.throwIf(!banStatus,ErrorCode.OPERATION_ERROR);

        return true;
    }

    /**
     * 用户脱敏
     * @param currentUser
     * @return
     */
    public UserEntity getSafetyUser(UserEntity currentUser){
        UserEntity safetyUser = new UserEntity();
        BeanUtils.copyProperties(currentUser,safetyUser);
        safetyUser.setUserPassword(null);
        safetyUser.setSecretKey(null);
        return safetyUser;
    }
}
